#include "../lib/useHead.h"
#include "lex.h"

Lex::Lex()
{
	resetLex();
    _lines.clear();
	_token_map[':'] = TOKEN_TYPE_COLON;
	_token_map['['] = TOKEN_TYPE_LBRACKET;
	_token_map[']'] = TOKEN_TYPE_RBRACKET;
	_token_map[','] = TOKEN_TYPE_COMMA;
	_token_map['{'] = TOKEN_TYPE_LBRACE;
	_token_map['}'] = TOKEN_TYPE_RBRACE;

	_Ident_map["Func"] = TOKEN_TYPE_FUNC;
	_Ident_map["Var"] = TOKEN_TYPE_VAR;
	_Ident_map["_RetVal"] = TOKEN_TYPE_REG_RETVAL;
	_Ident_map["SetStackSize"] = TOKEN_TYPE_SETSTACKSIZE;
	_Ident_map["Param"] = TOKEN_TYPE_PARAM;

}

void  Lex::saveToken(Token token)
{
    _svToken = token;
}

void Lex::addInstr(vector<string> &nameList)
{
	for (int i = 0; i < nameList.size(); ++i)
		_Ident_map[nameList[i]] = TOKEN_TYPE_INSTR;
}


void Lex::skipLine()
{
	++_line;
	_col = 0;
}


void Lex::resetLex()
{
	_svToken = TOKEN_TYPE_NONE;
	_line = _col = 0;
	//_lines.clear();
}


void Lex::stripComments(char *line)
{
	unsigned int i;
	bool     inString = false;

	for (i = 0; i < strlen(line); ++i) {
		if (line[i] == '"')
			inString = !inString;

		if (line[i] == _comment) {
			if (!inString) {
				line[i] = '\n';
				line[i+1] = '\0';
			}
		}
	}
}


void  Lex::trim(char *line)
{
	char *s, *p = line;

	if (line == NULL)
		return;

	while (*p && isspace(*p)) ++p;
	if (*p == '\0') {
		line[0] = '\0';
		return;
	}

	s = strlen(p) + p -1;
	while (isspace(*s)) --s;
	s[1] = '\0';

	memmove(line, p, s-p+2);
}


int Lex::loadFile(const char *file)
{
	char *p;
	char buf[1024*4];
	int  lineno = 0;
	TLine line;
	FILE *fp = fopen(file, "r");
	
	if (fp == NULL) {
		return -1;
	}

	while (fgets(buf, sizeof(buf), fp)) {
		++lineno;
		if (buf[0] == '\n' || buf[0] == _comment)
			continue;

		stripComments(buf);
		trim(buf);

		if (buf[0] == '\0')
			continue;

		line._data = buf;
		line._lineInFile = lineno;

		_lines.push_back(line);
	}

	fclose(fp);
	return 0;
}


int  Lex::getCurrLine()
{
	if (_line < _lines.size())
		return _lines[_line]._lineInFile;

	return -1;
}


#define INRANGE(x, min, max)  ((x) <= max && (x) >= min)


Token Lex::getNextToken()
{
	int start, end;
	Token ret;

	char c;
	
	if (_svToken != TOKEN_TYPE_NONE) {
		ret = _svToken;
		_svToken = TOKEN_TYPE_NONE;
		return ret;
	}

	if (_line == _lines.size())
		return END_OF_TOKEN_STREAM;

	string &data = _lines[_line]._data;	
	if (_col == data.size()) {
		++_line;
		_col = 0;
		return TOKEN_TYPE_NEWLINE;
	}

	c = data[_col++];
	while (isspace(c)) {
		if (_col == data.size()) {
			++_line;
			_col = 0;
			return TOKEN_TYPE_NEWLINE;
		}
		c = data[_col++];
	}
	start = _col - 1;

	if (_token_map.find(c) != _token_map.end())
		return _token_map[c];

	if (c == '-' || c == '+' || c == '.' || INRANGE(c, '0', '9')) {
		ret = TOKEN_TYPE_INT;
		while (_col < data.size()) {
			c = data[_col++];
			if (INRANGE(c, '0', '9'))
				continue;
			if (c == '.') {
				if (ret == TOKEN_TYPE_FLOAT)
					return TOKEN_TYPE_INVALID;

				ret = TOKEN_TYPE_FLOAT;
				continue;
			}
			--_col;
			break;
		}
		_tempName = data.substr(start, _col-start);
		return ret;
	}

	if (c == '"') {
		ret = TOKEN_TYPE_STRING;
		while (_col < data.size()) {
			c = data[_col++];
            if (c == '\\') 
                ++_col;    
            
			if (c == '"')
				break;
		}
		if (c != '"')
			return TOKEN_TYPE_INVALID;
		_tempName = data.substr(start+1, _col-start-2);
		return ret;
	}	


	if (INRANGE(c, 'a', 'z') || INRANGE(c, 'A', 'Z') || c == '_') {
		ret = TOKEN_TYPE_IDENT;
		while (_col < data.size()) {
			c = data[_col++];
			if (INRANGE(c, 'a', 'z') || INRANGE(c, 'A', 'Z') ||
				c == '_' || INRANGE(c, '0', '9'))
				continue;
			--_col;
			break;
		}
		_tempName = data.substr(start, _col-start);
		
		if (_Ident_map.find(_tempName) != _Ident_map.end())
			ret = _Ident_map[_tempName];
		return ret;
	}

	return TOKEN_TYPE_INVALID;
}	
